//===============================================================================
// Microsoft patterns & practices
// Web Client Software Factory
//-------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//-------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using WCSFContrib.PageFlow.WorkflowFoundation.Activities;
using WCSFContrib.PageFlow.Utils;

namespace WCSFContrib.PageFlow.WorkflowFoundation
{
    /// <summary>
    /// Implementation of a page flow definition that relies on the <see cref="Activities.PageFlow">PageFlow activity</see>.
    /// </summary>
    /// <remarks>This class is used to query the page flow metadata and is accessible through the <see cref="WorkflowFoundationPageFlow.Definition"></see> property.</remarks>
	public class WorkflowFoundationPageFlowDefinition : IPageFlowDefinition
	{
		private Page _abortPage;
		private Activities.PageFlow _definition;

        /// <summary>
        /// Initializes a new instance of the <see cref="WorkflowFoundationPageFlowDefinition"></see> given a <see cref="Activities.PageFlow">PageFlow activity</see>. 
        /// </summary>
        /// <param name="definition">A <see cref="Activities.PageFlow">PageFlow</see></param>
        /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"></see></exception>
		public WorkflowFoundationPageFlowDefinition(Activities.PageFlow definition)
		{
			if (definition == null)
				throw new ArgumentNullException("definition");

			_definition = definition;
			_abortPage = new Page("PageFlow.AbortPage", definition.AbortUrl);
		}

        /// <summary>
        /// Gets the page flow abandon behavior.
        /// </summary>
		public AbandonBehavior Abandonable
		{
			get { return _definition.Abandonable; }
		}

        /// <summary>
        /// Gets the abort page defined in the page flow.
        /// </summary>
        /// <remarks>See <see cref="Activities.PageFlow.AbortUrl">AbortUrl</see> property</remarks>
		public Page AbortPage
		{
			get { return _abortPage; }
		}

        /// <summary>
        /// Gets true if the page flow is constrained
        /// </summary>
        /// <remarks>See <see cref="Activities.PageFlow.Constrained">Constrained</see> property</remarks>
        public bool Constrained
		{
			get { return _definition.Constrained; }
		}

        /// <summary>
        /// Gets the name of the page flow
        /// </summary>
        /// <remarks>See <see cref="Activity.Name">Name</see> property of the <see cref="Activity"></see></remarks>
        public string Name
		{
			get { return _definition.Name; }
		}

        /// <summary>
        /// Gets the underlying page flow definition
        /// </summary>
        /// <remarks>See <see cref="Activities.PageFlow"></see></remarks>
        public Activities.PageFlow Definition
        {
            get { return _definition; }
        }

        /// <summary>
        /// Gets the url to send users to when they attempt to visit a page that is part of the 
        /// page flow when there is no running instance of the page flow.
        /// </summary>
        /// <remarks>See <see cref="Activities.PageFlow.NotRunningUrl">NotRunningUrl</see> property</remarks>
        public string NotRunningRedirect
		{
			get { return _definition.NotRunningUrl; }
		}

        /// <summary>
        /// Gets a collection of pages defined in the page flow
        /// </summary>
        /// <remarks>The collection is filled with <see cref="PageState"></see> activities</remarks>
		public ReadOnlyCollection<Page> Pages
		{
			get
			{
				List<Page> pages = new List<Page>();

				foreach (Activity activity in _definition.Activities)
				{
					PageState pageActivity = activity as PageState;

					if (pageActivity != null)
					{
						pages.Add(GetPage(pageActivity.Name));
					}
				}

				return pages.AsReadOnly();
			}
		}

        /// <summary>
        /// Gets the start page defined in the page flow.
        /// </summary>
        /// <remarks>See <see cref="StateMachineWorkflowActivity.InitialStateName">InitialStateName</see> property.</remarks>
        public Page StartPage
		{
			get { return GetPage(_definition.InitialStateName); }
		}

        /// <summary>
        /// Returns true if the <paramref name="page"/> belongs to the page flow.
        /// </summary>
        /// <param name="page">The page to look for</param>
        /// <returns>True if the page was found</returns>
        /// <remarks>The check is done by the <see cref="Page.Name">Name</see> property.</remarks>
		public bool ContainsPage(Page page)
		{
			if (page == null)
				throw new ArgumentNullException("page");

			Activity pageActivity = GetActivityByName(page.Name);
			return pageActivity != null;
		}

        /// <summary>
        /// Returns true if the <paramref name="url"/> belongs to the page flow.
        /// </summary>
        /// <param name="url">The url to look for in the page flow</param>
        /// <returns>True if the page was found</returns>
        /// <remarks>The check is done by the <see cref="Page.Url">Url</see> property.</remarks>
        public bool ContainsUrl(string url)
		{
			return GetActivityByUrl(url) != null;
		}

        /// <summary>
        /// Gets a <see cref="Page"></see> given the page flow state name.
        /// </summary>
        /// <param name="stateName">The <see cref="Activity.Name"></see>.</param>
        /// <returns>A <see cref="Page"></see>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="stateName"/> is <see langword="null"></see>.</exception>
		public Page GetPage(string stateName)
		{
			Guard.ArgumentNotNullOrEmptyString(stateName, "stateName");

			PageState pageActivity = GetActivityByName(stateName);
			return GetPage(pageActivity);
		}

        /// <summary>
        /// Gets a <see cref="Page"></see> given the <see cref="PageState">PageState activity</see>.
        /// </summary>
        /// <param name="pageState">The <see cref="PageState">PageState activity</see>.</param>
        /// <returns>A <see cref="Page"></see>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="pageState"/> is <see langword="null"></see>.</exception>
        public Page GetPage(PageState pageState)
		{
			Guard.ArgumentNotNull(pageState, "pageState");

			List<string> transitions = new List<string>();
			foreach (Activity activity in pageState.Activities)
			{
				if (activity is EventDrivenActivity)
				{
					transitions.Add(activity.Name);
				}
			}

			return new Page(pageState.Name, pageState.Url, pageState.DefaultTransition, transitions);
		}

        /// <summary>
        /// Gets a <see cref="Page"></see> given a url.
        /// </summary>
        /// <param name="url">The url to find</param>
        /// <returns>A <see cref="Page"></see></returns>
        /// <exception cref="ArgumentNullException"><paramref name="url"/> is <see langword="null"></see></exception>
		public Page GetPageFromUrl(string url)
		{
			Guard.ArgumentNotNullOrEmptyString(url, "url");

			PageState pageActivity = GetActivityByUrl(url);
			return GetPage(pageActivity);
		}

        /// <summary>
        /// Gets the page flow <see cref="Type"></see>
        /// </summary>
		public Type PageFlowType
		{
			get { return _definition.GetType(); }
		}

        /// <summary>
        /// Returns a <see cref="PageState"></see> activity given the url.
        /// </summary>
        /// <param name="rawUrl">The url.</param>
        /// <returns>A <see cref="PageState"></see>.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#")]
		public PageState GetActivityByUrl(string rawUrl)
		{
			return (PageState)_definition.Activities.Find(new Predicate<Activity>(delegate(Activity activity)
				{
					return string.Compare(((PageState)activity).Url, rawUrl, true, CultureInfo.CurrentCulture) == 0;
				}));
		}

        /// <summary>
        /// Returns a <see cref="PageState"></see> activity given the name of the state.
        /// </summary>
        /// <param name="name">The state activity name.</param>
        /// <returns>A <see cref="PageState"></see>.</returns>
        public PageState GetActivityByName(string name)
		{
			return (PageState)_definition.Activities.Find(new Predicate<Activity>(delegate(Activity activity)
				{
					return string.Compare(activity.Name, name, true, CultureInfo.CurrentCulture) == 0;
				}));
		}
	}
}
